<!doctype html>
<meta charset=utf-8>
<title></title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src="RTCPeerConnection-helper.js"></script>
<script>
"use strict";

const kSmallTimeoutMs = 100;

promise_test(async t => {
  const offerer = new RTCPeerConnection();
  t.add_cleanup(() => offerer.close());

  const signalingStateChangeEvent
      = new EventWatcher(t, offerer, 'signalingstatechange')
      .wait_for('signalingstatechange');
  await offerer.setLocalDescription();
  await signalingStateChangeEvent;
  assert_equals(offerer.signalingState, 'have-local-offer');
}, "Parameterless SLD() in 'stable' goes to 'have-local-offer'");

promise_test(async t => {
  const offerer = new RTCPeerConnection();
  t.add_cleanup(() => offerer.close());

  await offerer.setLocalDescription();
  assert_not_equals(offerer.pendingLocalDescription, null);
  assert_equals(offerer.pendingLocalDescription, offerer.pendingLocalDescription);
}, "Parameterless SLD() in 'stable' sets pendingLocalDescription");

promise_test(async t => {
  const offerer = new RTCPeerConnection();
  t.add_cleanup(() => offerer.close());

  const transceiver = offerer.addTransceiver('audio');
  assert_equals(transceiver.mid, null);
  await offerer.setLocalDescription();
  assert_not_equals(transceiver.mid, null);
}, "Parameterless SLD() in 'stable' assigns transceiver.mid");

promise_test(async t => {
  const offerer = new RTCPeerConnection();
  t.add_cleanup(() => offerer.close());
  const answerer = new RTCPeerConnection();
  t.add_cleanup(() => answerer.close());

  await answerer.setRemoteDescription(await offerer.createOffer());
  const signalingStateChangeEvent
      = new EventWatcher(t, answerer, 'signalingstatechange')
      .wait_for('signalingstatechange');
  await answerer.setLocalDescription();
  await signalingStateChangeEvent;
  assert_equals(answerer.signalingState, 'stable');
}, "Parameterless SLD() in 'have-remote-offer' goes to 'stable'");

promise_test(async t => {
  const offerer = new RTCPeerConnection();
  t.add_cleanup(() => offerer.close());
  const answerer = new RTCPeerConnection();
  t.add_cleanup(() => answerer.close());

  await answerer.setRemoteDescription(await offerer.createOffer());
  await answerer.setLocalDescription();
  assert_not_equals(answerer.currentLocalDescription, null);
  assert_equals(answerer.currentLocalDescription, answerer.currentLocalDescription);
}, "Parameterless SLD() in 'have-remote-offer' sets currentLocalDescription");

promise_test(async t => {
  const offerer = new RTCPeerConnection();
  t.add_cleanup(() => offerer.close());
  const answerer = new RTCPeerConnection();
  t.add_cleanup(() => answerer.close());

  offerer.addTransceiver('audio');
  const onTransceiverPromise = new Promise(resolve =>
      answerer.ontrack = e => resolve(e.transceiver));
  await answerer.setRemoteDescription(await offerer.createOffer());
  const transceiver = await onTransceiverPromise;
  await answerer.setLocalDescription();
  assert_equals(transceiver.currentDirection, 'recvonly');
}, "Parameterless SLD() in 'have-remote-offer' sets " +
   "transceiver.currentDirection");

promise_test(async t => {
  const offerer = new RTCPeerConnection();
  t.add_cleanup(() => offerer.close());

  const offer = await offerer.createOffer();
  await offerer.setLocalDescription();
  // assert_true() is used rather than assert_equals() so that if the assertion
  // fails, the -expected.txt file is not different on each run.
  assert_true(offerer.pendingLocalDescription.sdp == offer.sdp,
              "offerer.pendingLocalDescription.sdp == offer.sdp");
}, "Parameterless SLD() uses [[LastCreatedOffer]] if it is still valid");

promise_test(async t => {
  const offerer = new RTCPeerConnection();
  t.add_cleanup(() => offerer.close());
  const answerer = new RTCPeerConnection();
  t.add_cleanup(() => answerer.close());

  await answerer.setRemoteDescription(await offerer.createOffer());
  const answer = await answerer.createAnswer();
  await answerer.setLocalDescription();
  // assert_true() is used rather than assert_equals() so that if the assertion
  // fails, the -expected.txt file is not different on each run.
  assert_true(answerer.currentLocalDescription.sdp == answer.sdp,
              "answerer.currentLocalDescription.sdp == answer.sdp");
}, "Parameterless SLD() uses [[LastCreatedAnswer]] if it is still valid");

promise_test(async t => {
  const offerer = new RTCPeerConnection();
  offerer.close();
  try {
    await offerer.setLocalDescription();
    assert_not_reached();
  } catch (e) {
    assert_equals(e.name, "InvalidStateError");
  }
}, "Parameterless SLD() rejects with InvalidStateError if already closed");

promise_test(async t => {
  const offerer = new RTCPeerConnection();
  t.add_cleanup(() => offerer.close());

  const p = Promise.race([
    offerer.setLocalDescription(),
    new Promise(r => t.step_timeout(() => r("timeout"), kSmallTimeoutMs))
  ]);
  offerer.close();
  assert_equals(await p, "timeout");
}, "Parameterless SLD() never settles if closed while pending");

promise_test(async t => {
  const offerer = new RTCPeerConnection();
  t.add_cleanup(() => offerer.close());
  const answerer = new RTCPeerConnection();
  t.add_cleanup(() => answerer.close());

  // Implicitly create an offer.
  await offerer.setLocalDescription();
  await answerer.setRemoteDescription(offerer.pendingLocalDescription);
  // Implicitly create an answer.
  await answerer.setLocalDescription();
  await offerer.setRemoteDescription(answerer.currentLocalDescription);
}, "Parameterless SLD() in a full O/A exchange succeeds");

promise_test(async t => {
  const answerer = new RTCPeerConnection();
  try {
    await answerer.setRemoteDescription();
    assert_not_reached();
  } catch (e) {
    assert_equals(e.name, "TypeError");
  }
}, "Parameterless SRD() rejects with TypeError.");

promise_test(async t => {
  const offerer = new RTCPeerConnection();
  const {sdp} = await offerer.createOffer();
  new RTCSessionDescription({type: "offer", sdp});
  try {
    new RTCSessionDescription({sdp});
    assert_not_reached();
  } catch (e) {
    assert_equals(e.name, "TypeError");
  }
}, "RTCSessionDescription constructed without type throws TypeError");

</script>
